package com.eastmoney.gamecode.service.impl;

import com.eastmoney.gamecode.constant.GameConstants;
import com.eastmoney.gamecode.enums.MapElementEnums;
import com.eastmoney.gamecode.enums.MoveTypeEnums;
import com.eastmoney.gamecode.service.GameLogic;
import com.eastmoney.gamecode.util.GameUtils;
import com.eastmoney.gamecode.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 游戏逻辑
 *
 * @author
 * @date 2023-09
 */
@Slf4j
@Service
public class GameLogicImpl implements GameLogic {

    private static int calcD(int m, int n, int offset) {
        if (m - n < 0) {
            return m - n + offset;
        } else if (m - n > 0) {
            return m - n - offset;
        }
        return 0;
    }

    @Override
    public GameResponse processGame(GameRequest gameRequest) {
        long intfStartTime = System.currentTimeMillis();
        // 获取自己的坦克对象
        try {
            TankSateVo myTank = gameRequest.getMyTank();
            if (myTank == null) {
                return new GameResponse(MoveTypeEnums.STOP.getCode(), false);
            }
            // 在这里重新计算移动和射击的决策
            // TODO: 2023/9/29
            String moveType = determineMoveType(gameRequest);
            if (System.currentTimeMillis() - intfStartTime > 95) {
                log.warn("determineMoveType 方法耗时过长:{}", System.currentTimeMillis() - intfStartTime);
            }
            // TODO: 2023/9/29
            boolean shouldShoot = true;
//        boolean shouldShoot = shouldShoot(gameRequest, myTank);
            // 创建并返回响应对象
            GameResponse gameResponse = new GameResponse(moveType, shouldShoot);
            log.info("moveType: {}. shouldShoot: {}", moveType, shouldShoot);
            return gameResponse;

        } catch (Exception e) {
            log.error("eee", e);
            throw e;
        } finally {
            log.info("接口耗时：{}", System.currentTimeMillis() - intfStartTime);
        }
    }

    /**
     * 计算可能遭受的攻击
     *
     * @param otherTank       地方坦克
     * @param myTank          我方坦克
     * @param safeAreaStateVo 安全区
     * @return 伤害量大小
     */
    private int calcPossibleDamage(TankSateVo otherTank, TankSateVo myTank, SafeAreaStateVo safeAreaStateVo) {
        // 伤害计算公式：（攻击力 - 防御力）+ 毒圈扣血量，伤害值会扣减到生命值中。
        return otherTank.getAtk() - myTank.getDef() + (willSufferPoison(safeAreaStateVo, myTank) ? 5 : 0);
    }

    /**
     * 是否将会遭受毒圈扣血？
     *
     * @param safeAreaStateVo
     * @param myTank
     * @return
     */
    private boolean willSufferPoison(SafeAreaStateVo safeAreaStateVo, TankSateVo myTank) {
        // TODO: 2023/9/29 毒圈内扣血：坦克位于毒圈内超过20个接口调用周期，扣5血量
        return myTank.isInPoisonCircle(safeAreaStateVo);
    }

    private String determineMoveType(GameRequest gameRequest) {
        TankSateVo myTank = gameRequest.getMyTank();
        // 解析请求参数
        List<BulletSateVo> bulletStates = gameRequest.getBulletSateVoList();

        log.info("当前时间：{}，安全区域的半径：{}", System.currentTimeMillis(), gameRequest.getSafeAreaStateVo().getR());

        //找到会碰上子弹的方向
        Set<Loc> meetBulletLocSet = GameUtils.calBulletNearBy(bulletStates, myTank);

        Loc next = GameUtils.calcNextLoc(gameRequest, meetBulletLocSet);
        //todo 安全区 躲避 主动攻击  超时
        if (Objects.isNull(next)) {
            int[][] distance = gameRequest.getDistance();
            Loc cur = gameRequest.getMyTank().toLoc();
            for (Loc direction : Loc.DIRECTIONS) {
                Loc move = cur.move(direction);
                if (distance[move.y][move.x] < 20000 && !meetBulletLocSet.contains(move)) {
                    next = move;
                    break;
                }
            }
            if (Objects.isNull(next)) {
                log.warn("无可用方向");
                return MoveTypeEnums.STOP.getCode();
            }
        }
        Loc cur = myTank.toLoc();
        String format = String.format("%s,%s", next.x - cur.x, next.y - cur.y);
        MoveTypeEnums moveTypeEnums = Loc.DIREC_MAP.get(format);

        return moveTypeEnums.getCode();


//        calcPossibleDamage(myTank, myTank, gameRequest.getSafeAreaStateVo());
//
//
//        //判断各种移动类型操作后的预期结果
//        String[] moveTypes = {"TOP", "DOWN", "LEFT", "RIGHT", "STOP"};
//        List<MoveResultTankSateVo> moveCostVoList = new ArrayList<>();
//
//        for (String moveType : moveTypes) {
//            MoveResultTankSateVo moveResultVo = calcMoveResult(gameRequest, myTank, moveType);
//            moveCostVoList.add(moveResultVo);
//        }
//        //选择成本最低的移动方向
//        String result = MoveTypeEnums.STOP.getCode();
//        return result;
    }

    private MoveResultTankSateVo calcMoveResult(GameRequest gameRequest, TankSateVo myTank, String moveType) {
        MoveResultTankSateVo moveResultVo = createTankMoveResult(myTank);
        moveResultVo.setMoveType(moveType);
        switch (MoveTypeEnums.valueOf(moveType)) {
            case TOP:  //向上移动一步
                moveResultVo.setY(moveResultVo.getY() - GameConstants.TANK_MOVEMENT_SPEED);
                break;
            case DOWN: // 向下移动一步
                moveResultVo.setY(moveResultVo.getY() + GameConstants.TANK_MOVEMENT_SPEED);
                break;
            case LEFT: // 向左移动一步
                moveResultVo.setX(moveResultVo.getX() - GameConstants.TANK_MOVEMENT_SPEED);
                break;
            case RIGHT: // 向右移动一步
                moveResultVo.setX(moveResultVo.getX() + GameConstants.TANK_MOVEMENT_SPEED);
                break;
            case STOP: // 向右移动一步
                break;
        }
        // 获取自己的坦克位置和大小
        int myX = moveResultVo.getX();
        int myY = moveResultVo.getY();
        int myWidth = moveResultVo.getWidth();
        int myHeight = moveResultVo.getHeight();
        if (myX < 0 || myY < 0) {
            moveResultVo.setAllowMove(false);
            return moveResultVo;
        }
        // 地图元素
        if (isCollisionWithTerrain(myX, myY, myWidth, myHeight, gameRequest.getMap())) {
            moveResultVo.setAllowMove(false);
            return moveResultVo;
        }
        // 遍历其他的坦克，判断是否与之碰撞，如果是，则停止移动，并调整位置
        for (TankSateVo otherTank : gameRequest.getTankSatesVoList()) {
            if (!otherTank.getTankId().equals(moveResultVo.getTankId())) {
                int otherTankX = otherTank.getX();
                int otherTankY = otherTank.getY();
                int otherTankWidth = otherTank.getWidth();
                int otherTankHeight = otherTank.getHeight();
                if (isCollisionWithTank(myX, myY, myWidth, myHeight, otherTankX, otherTankY, otherTankWidth, otherTankHeight)) {
                    moveResultVo.setAllowMove(false);
                    return moveResultVo;
                }
            }
        }
        // 遍历其他的坦克对象，判断是否有敌方坦克在附近（距离小于50），如果有，则向敌方坦克移动
        for (TankSateVo otherTank : gameRequest.getTankSatesVoList()) {
            if (!otherTank.getTankId().equals(moveResultVo.getTankId())) {
                int otherX = otherTank.getX();
                int otherY = otherTank.getY();
                if (distance(myX, myY, otherX, otherY) < 50) {
                    String targetDirection = getDirection(myX, myY, otherX, otherY);
                    if (targetDirection.equalsIgnoreCase(moveType)) {
                        moveResultVo.setScore(moveResultVo.getScore() + 10);
                    }
                }
            }
        }
        // 毒圈
        if (moveResultVo.isInPoisonCircle(gameRequest.getSafeAreaStateVo())) {
            moveResultVo.setScore(moveResultVo.getScore() - 10);
        }
        // 玩家子弹
        Iterator<BulletSateVo> iterator = gameRequest.getBulletSateVoList().iterator();
        while (iterator.hasNext()) {
            BulletSateVo bullet = iterator.next();
            int bulletX = bullet.getX();
            int bulletY = bullet.getY();
            int bulletWidth = bullet.getWidth();
            int bulletHeight = bullet.getHeight();
            if (isCollisionWithBullet(myX, myY, myWidth, myHeight, bulletX, bulletY, bulletWidth, bulletHeight)) {
                moveResultVo.setScore(moveResultVo.getScore() - 100);
            }
        }
        // 遍历道具对象，判断是否有道具在附近（距离小于50），如果有，则向道具移动
        for (PropVo prop : gameRequest.getPropVoList()) {
            int propX = prop.getX();
            int propY = prop.getY();
            int propWidth = prop.getWidth();
            int propHeight = prop.getHeight();
            if (distance(myX, myY, propX, propY) < 50) {
                String targetDirection = getDirection(myX, myY, propX, propY);
                if (targetDirection.equalsIgnoreCase(moveType)) {
                    moveResultVo.setScore(moveResultVo.getScore() + 10);
                }
            }
        }

        // 击杀奖励
        handleKillRewards();
        return moveResultVo;
    }

    // 获取朝目标移动的方向
    private String getDirection(int myX, int myY, int otherX, int otherY) {
        // 计算两点之间的角度，使用Math.atan2()方法
        double angle = Math.atan2(otherY - myY, otherX - myX);

        // 根据角度的范围，返回一个字符串，表示方向
        // 0:右，1:上，2:左，3:下
        if (angle >= -Math.PI / 4 && angle < Math.PI / 4) {
            return "RIGHT";
        } else if (angle >= Math.PI / 4 && angle < 3 * Math.PI / 4) {
            return "TOP";
        } else if (angle >= 3 * Math.PI / 4 || angle < -3 * Math.PI / 4) {
            return "LEFT";
        } else {
            return "DOWN";
        }
    }

    private MoveResultTankSateVo createTankMoveResult(TankSateVo myTank) {
        MoveResultTankSateVo moveResultTankSateVo = new MoveResultTankSateVo();
        moveResultTankSateVo.setAllowMove(true);
        moveResultTankSateVo.setTankId(myTank.getTankId());
        moveResultTankSateVo.setX(myTank.getX());
        moveResultTankSateVo.setY(myTank.getY());
        moveResultTankSateVo.setWidth(myTank.getWidth());
        moveResultTankSateVo.setHeight(myTank.getHeight());
        moveResultTankSateVo.setHp(myTank.getHp());
        moveResultTankSateVo.setAtk(myTank.getAtk());
        moveResultTankSateVo.setDef(myTank.getDef());
        moveResultTankSateVo.setMovingDirect(myTank.getMovingDirect());
        return moveResultTankSateVo;
    }

    // 计算自己的坦克和一个道具之间的距离
    private double distance(int myX, int myY, int propX, int propY) {
        // 计算两点之间的水平距离和垂直距离，使用Math.abs()方法
        int dx = Math.abs(myX - propX);
        int dy = Math.abs(myY - propY);

        // 根据勾股定理，计算两点之间的直线距离，使用Math.sqrt()方法
        return Math.sqrt(dx * dx + dy * dy);
    }

    // 根据坦克状态、安全区位置和地图确定是否射击
    private boolean shouldShoot(GameRequest gameRequest, TankSateVo myTank) {
        // 在这里根据游戏规则和当前情况，判断自己的坦克是否需要发射子弹
        // 可以根据坦克当前位置、地图情况、安全区情况等来决定是否射击
        // 这部分逻辑需要根据具体游戏规则来实现
        String tankId = gameRequest.getTankId();
        List<TankSateVo> tankStates = gameRequest.getTankSatesVoList();
        GameMap map = gameRequest.getMap();

        // 获取自己的坦克位置和大小
        int myX = myTank.getX();
        int myY = myTank.getY();
        int myWidth = myTank.getWidth();
        int myHeight = myTank.getHeight();


        // 遍历其他的坦克对象，判断是否有敌方坦克在射程内（距离小于100），如果有，则需要射击
        for (TankSateVo otherTank : tankStates) {
            if (!otherTank.getTankId().equals(tankId)) {
                int otherX = otherTank.getX();
                int otherY = otherTank.getY();
                int otherWidth = otherTank.getWidth();
                int otherHeight = otherTank.getHeight();
                if (distance(myX, myY, otherX, otherY) < 100) {
                    return true;
                }
            }
        }

        // 判断自己的坦克是否有障碍物在射击方向上，如果有，则不需要射击
        // 假设自己的坦克的方向是myTank.getDirection()，可以是"TOP"、"DOWN"、"LEFT"、"RIGHT"之一
        switch (myTank.getMovingDirect().getCode()) {
            case "TOP": // 向上射击
                // 判断是否有地形或其他坦克在上方，如果有，则不射击
                for (int i = myY - 10; i >= 0; i -= 10) {
                    if (isCollisionWithTerrain(myX, i, myWidth, 10, map)) {
                        return false;
                    }
                    for (TankSateVo otherTank : tankStates) {
                        if (!otherTank.getTankId().equals(tankId)) {
                            int otherX = otherTank.getX();
                            int otherY = otherTank.getY();
                            int otherWidth = otherTank.getWidth();
                            int otherHeight = otherTank.getHeight();
                            if (isCollisionWithTank(myX, i, myWidth, 10, otherX, otherY, otherWidth, otherHeight)) {
                                return false;
                            }
                        }
                    }
                }
                break;
            case "DOWN": // 向下射击
                // 判断是否有地形或其他坦克在下方，如果有，则不射击
                for (int i = myY + myHeight; i < map.ySize() * 10; i += 10) {
                    if (isCollisionWithTerrain(myX, i, myWidth, 10, map)) {
                        return false;
                    }
                    for (TankSateVo otherTank : tankStates) {
                        if (!otherTank.getTankId().equals(tankId)) {
                            int otherX = otherTank.getX();
                            int otherY = otherTank.getY();
                            int otherWidth = otherTank.getWidth();
                            int otherHeight = otherTank.getHeight();
                            if (isCollisionWithTank(myX, i, myWidth, 10, otherX, otherY, otherWidth, otherHeight)) {
                                return false;
                            }
                        }
                    }
                }
                break;
            case "LEFT": // 向左射击
                // 判断是否有地形或其他坦克在左方，如果有，则不射击
                for (int i = myX - 10; i >= 0; i -= 10) {
                    if (isCollisionWithTerrain(i, myY, 10, myHeight, map)) {
                        return false;
                    }
                    for (TankSateVo otherTank : tankStates) {
                        if (!otherTank.getTankId().equals(tankId)) {
                            int otherX = otherTank.getX();
                            int otherY = otherTank.getY();
                            int otherWidth = otherTank.getWidth();
                            int otherHeight = otherTank.getHeight();
                            if (isCollisionWithTank(i, myY, 10, myHeight, otherX, otherY, otherWidth, otherHeight)) {
                                return false;
                            }
                        }
                    }
                }
                break;
            case "RIGHT": // 向右射击
                // 判断是否有地形或其他坦克在右方，如果有，则不射击
                for (int i = myX + myWidth; i < map.xSize() * 10; i += 10) {
                    if (isCollisionWithTerrain(i, myY, 10, myHeight, map)) {
                        return false;
                    }
                    for (TankSateVo otherTank : tankStates) {
                        if (!otherTank.getTankId().equals(tankId)) {
                            int otherX = otherTank.getX();
                            int otherY = otherTank.getY();
                            int otherWidth = otherTank.getWidth();
                            int otherHeight = otherTank.getHeight();
                            if (isCollisionWithTank(i, myY, 10, myHeight, otherX, otherY, otherWidth, otherHeight)) {
                                return false;
                            }
                        }
                    }
                }
                break;
        }

        // 如果以上都不满足，则随机选择是否射击，或者保持不射击
        Random random = new Random();
        double p = random.nextDouble(); // 生成一个0到1之间的随机数
        // 有50%的概率射击
        // 有50%的概率不射击
        return p < 0.5;
    }


    // 判断一个物体是否与地形碰撞，根据物体的位置和大小以及地图进行判定，如果有重合区域则代表发生碰撞
    private boolean isCollisionWithTerrain(int x, int y, int width, int height, GameMap map) {
        // 计算物体所占的地图格子的范围
        int left = x / 10;
        int right = (x + width - 1) / 10;
        int top = y / 10;
        int bottom = (y + height - 1) / 10;

        // 遍历物体所占的地图格子，判断是否有不可穿越的地形
        for (int i = left; i <= right; i++) {
            for (int j = top; j <= bottom; j++) {
                // 如果地图格子是墙或河，则表示碰撞
                if (map.loc(i, j) == MapElementEnums.W
                        || map.loc(i, j) == MapElementEnums.I) {
                    return true;
                }
            }
        }

        // 否则，表示没有碰撞
        return false;
    }

    // 判断两个物体是否相互碰撞，根据两个物体的位置和大小进行判定，如果有重合区域则代表发生碰撞
    private boolean isCollisionWithTank(int x1, int y1, int width1, int height1, int x2, int y2, int width2, int height2) {
        // 计算两个物体的边界
        int left1 = x1;
        int right1 = x1 + width1 - 1;
        int top1 = y1;
        int bottom1 = y1 + height1 - 1;

        int left2 = x2;
        int right2 = x2 + width2 - 1;
        int top2 = y2;
        int bottom2 = y2 + height2 - 1;

        int maxX0 = Math.max(x1, x2);
        int minX1 = Math.min(right1, right2);
        int maxY0 = Math.max(y1, y2);
        int minY1 = Math.min(bottom1, bottom2);

        // 判断两个物体是否有重合区域，如果有，则表示碰撞
        return (minY1 - maxY0) > 0 && (minX1 - maxX0) * (minY1 - maxY0) >= 1;
    }

    // 判断一个坦克是否与一个子弹碰撞
    private boolean isCollisionWithBullet(int tankX, int tankY, int tankWidth, int tankHeight, int bulletX, int bulletY, int bulletWidth, int bulletHeight) {
        // 计算两个物体的边界
        int tankLeft = tankX;
        int tankRight = tankX + tankWidth - 1;
        int tankTop = tankY;
        int tankBottom = tankY + tankHeight - 1;

        int bulletLeft = bulletX;
        int bulletRight = bulletX + bulletWidth - 1;
        int bulletTop = bulletY;
        int bulletBottom = bulletY + bulletHeight - 1;

        // 判断两个物体是否有重合区域，如果有，则表示碰撞
        return tankLeft <= bulletRight && tankRight >= bulletLeft && tankTop <= bulletBottom && tankBottom >= bulletTop;

        // 否则，表示没有碰撞
    }

    /**
     * 截杀坦克奖励
     */
    private void handleKillRewards() {

    }
}
